package com.yanxu.inter;

import com.yanxu.common.SnmpParameter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.*;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 折戟沉沙铁未销
 * @version V1.0
 * @date 2023/11/9-2023
 * @Description: 发送请求并且处理返回数据
 */
@SuppressWarnings("all")
@Slf4j
public abstract class SnmpRequestHandle {

    public static final String AES256 = "aes256";
    public static final String AES192 = "aes192";
    public static final String AES128 = "aes128";
    public static final String _3DES = "3des";
    public static final String DES = "des";
    public static final String SHA = "sha";
    public static final String MD5 = "md5";

    public static Snmp snmp;
    static {
        try {
            DefaultUdpTransportMapping transportMapping = new DefaultUdpTransportMapping();
            transportMapping.setReceiveBufferSize(50 * 1024 * 1024);

            USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(
                    MPv3.createLocalEngineID(new OctetString("MyUniqueID" + System.currentTimeMillis()))), 0);

            snmp = new Snmp();
            snmp.addTransportMapping(transportMapping);
            SecurityProtocols.getInstance().addDefaultProtocols();
            snmp.getMessageDispatcher().addMessageProcessingModel(new MPv1());
            snmp.getMessageDispatcher().addMessageProcessingModel(new MPv2c());
            snmp.getMessageDispatcher().addMessageProcessingModel(new MPv3(usm));
            snmp.getMessageDispatcher().addCommandResponder(snmp);
            snmp.listen();
        } catch (IOException e) {
            e.printStackTrace();
            log.warn(e.getMessage());
        }
    }

    public Target createTarget(SnmpParameter p, Snmp snmp) {
        // v3 版本的
        if (p.getVersion() == SnmpConstants.version3) {
            UserTarget target = new UserTarget();
            target.setSecurityLevel(p.getSecurityLevel());
            OctetString securityName = new OctetString(p.getSecurityName());
            OctetString authPassphrase = null;
            OctetString privacyPassphrase = null;
            OID authProtocol = null;
            OID privacyProtocol = null;
            // 认证加密
            if (StringUtils.isNotBlank(p.getAuthPassphrase()) && p.getSecurityLevel() != SecurityLevel.NOAUTH_NOPRIV) {
                authPassphrase = new OctetString(p.getAuthPassphrase());
                if (StringUtils.equalsIgnoreCase(p.getAuthProtocol(), MD5)) {
                    authProtocol = AuthMD5.ID;
                }
                else if (StringUtils.equalsIgnoreCase(p.getAuthProtocol(), SHA)) {
                    authProtocol = AuthSHA.ID;
                }
            }
            // 加密算法
            if (StringUtils.isNotBlank(p.getPrivacyPassphrase()) && p.getSecurityLevel() == SecurityLevel.AUTH_PRIV) {
                privacyPassphrase = new OctetString(p.getPrivacyPassphrase());
                if (StringUtils.equalsIgnoreCase(p.getPrivacyProtocol(), DES)) {
                    privacyProtocol = PrivDES.ID;
                }
                else if (StringUtils.equalsIgnoreCase(p.getPrivacyProtocol(), _3DES)) {
                    privacyProtocol = Priv3DES.ID;
                }
                else if (StringUtils.equalsIgnoreCase(p.getPrivacyProtocol(), AES128)) {
                    privacyProtocol = PrivAES128.ID;
                }
                else if (StringUtils.equalsIgnoreCase(p.getPrivacyProtocol(), AES192)) {
                    privacyProtocol = PrivAES192.ID;
                }
                else if (StringUtils.equalsIgnoreCase(p.getPrivacyProtocol(), AES256)) {
                    privacyProtocol = PrivAES256.ID;
                }
            }
            target.setSecurityName(securityName);
            UsmUser user = new UsmUser(securityName, authProtocol, authPassphrase, privacyProtocol, privacyPassphrase);
            snmp.getUSM().addUser(securityName, user);
            return target;
        }

        // snmp v1 or v2c
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(p.getCommunity()));

        // snmp agent 地址
        Address address = new UdpAddress(p.getIp() + "/" + p.getPort());
        target.setAddress(address);
        // 通信不成功时的重试次数
        target.setRetries(p.getSnmpRetry());
        // 超时时间
        target.setTimeout(p.getSnmpTimeout());
        // 版本
        target.setVersion(p.getVersion());

        //返回
        return target;
    }

    public void execute(SnmpParameter snmpParameter) throws Exception {
        List<PDU> pduList = this.beforeRequest(snmpParameter);
        this.sendRequest(pduList, snmpParameter);
    }

    public void sendRequest(List<PDU> pduList, SnmpParameter snmpParameter) throws Exception {
        List<Object> requestResultList = new ArrayList<>();
        Target target = createTarget(snmpParameter, snmp);
        for (PDU pdu : pduList) {
            ResponseEvent responseEvent = snmp.send(pdu, target);
            if (responseEvent == null) {
                throw new RuntimeException("responseEvent is null error");
            }
            if (responseEvent.getResponse() == null) {
                throw new RuntimeException("responseEvent is null error");
            }
            if (responseEvent.getError() != null) {
                throw responseEvent.getError();
            }
            if (responseEvent.getResponse().getErrorStatus() != PDU.noError) {
                throw new RuntimeException("responseEvent.getResponse().getErrorStatus() is error");
            }

            List<? extends VariableBinding> variableBindings = responseEvent.getResponse().getVariableBindings();
            requestResultList.add(variableBindings);
        }
        this.handleResult(requestResultList);
    }

    public abstract List<PDU> beforeRequest(SnmpParameter snmpParameter) throws Exception;

    public abstract void handleResult(List<Object> result);

}
